﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="FiChartXmlSerializer.cs" company="">
//   
// </copyright>
// <summary>
//   The fi chart xml serializer.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Ficharts.Net
{
    using System;
    using System.Data;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Xml;

    using Ficharts.Net.Enums;
    using Ficharts.Net.Series;

    /// <summary>
    /// The fi chart xml serializer.
    /// </summary>
    internal class FiChartXmlSerializer
    {
        #region Methods

        /// <summary>
        /// The serialize.
        /// </summary>
        /// <param name="chart">
        /// The chart.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        internal string Serialize(SingleSeriesChart chart)
        {
            if (chart == null)
            {
                throw new ArgumentNullException();
            }

            string sAtt = "config";

            var stringWriter = new StringWriter();
            var xmlTextWriter = new XmlTextWriter(stringWriter);

            xmlTextWriter.WriteStartElement(sAtt);

            

            Type type = chart.GetType();
            this.SerializeProperty(chart, type, xmlTextWriter);

            

            foreach (PropertyInfo propertyInfoSup in type.GetProperties())
            {
                if (this.HasAttribute(propertyInfoSup, typeof(ChartPropertyAttribute)))
                {
                    xmlTextWriter.WriteStartElement(propertyInfoSup.Name.ToCamel());

                    foreach (FieldInfo fieldInfo in
                        propertyInfoSup.PropertyType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
                    {
                        if (this.HasAttribute(fieldInfo, typeof(GraphElementAttribute)))
                        {
                            object val = fieldInfo.GetValue(propertyInfoSup.GetValue(chart, null));
                            if (val != null)
                            {
                                xmlTextWriter.WriteAttributeString(fieldInfo.Name, this.ParseToXmlString(val));
                            }
                        }
                    }

                    xmlTextWriter.WriteEndElement();
                }
            }

            #region 坐标轴

            this.SerializeAxis(chart, xmlTextWriter);

            #endregion

            #region 图表类型

            this.SerializeSeries(chart, xmlTextWriter);

            #endregion

            #region 数据

            if (chart.DataType != DataType.Json)
            {
                this.SerializeData(chart, xmlTextWriter);
            }

            #endregion

            xmlTextWriter.WriteEndElement();
            return stringWriter.GetStringBuilder().ToString();
        }

        /// <summary>
        /// The has attribute.
        /// </summary>
        /// <param name="info">
        /// The info.
        /// </param>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private bool HasAttribute(MemberInfo info, Type type)
        {
            object[] attributes = info.GetCustomAttributes(true);
            return attributes.Any(attribute => attribute.GetType() == type);
        }

        /// <summary>
        /// The parse to xml string.
        /// </summary>
        /// <param name="val">
        /// The val.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        private string ParseToXmlString(object val)
        {
            if (val is bool?)
            {
                val = ((bool)val) ? "true" : "false";
            }

            if (val is decimal)
            {
                val = ((decimal)val).ToString();
            }

            return val.ToString();
        }

        /// <summary>
        /// 序列化坐标轴
        /// </summary>
        /// <param name="chart">
        /// </param>
        /// <param name="xmlTextWriter">
        /// </param>
        private void SerializeAxis(SingleSeriesChart chart, XmlTextWriter xmlTextWriter)
        {
            if (chart.XAxises != null && chart.XAxises.Count > 0)
            {
                xmlTextWriter.WriteStartElement("axis");
                foreach (AxisBase axis in chart.XAxises)
                {
                    Type typeColumn = axis.GetType();

                    xmlTextWriter.WriteStartElement("x");

                    foreach (PropertyInfo propertyInfo in typeColumn.GetProperties())
                    {
                        if (this.HasAttribute(propertyInfo, typeof(AxisElementAttribute)))
                        {
                            object val = propertyInfo.GetValue(axis, null);

                            if (val != null)
                            {
                                xmlTextWriter.WriteAttributeString(
                                    propertyInfo.Name.ToCamel(), this.ParseToXmlString(val));
                            }
                        }
                    }

                    xmlTextWriter.WriteEndElement();
                }

                foreach (AxisBase axis in chart.YAxises)
                {
                    Type typeColumn = axis.GetType();

                    xmlTextWriter.WriteStartElement("y");

                    foreach (PropertyInfo propertyInfo in typeColumn.GetProperties())
                    {
                        if (this.HasAttribute(propertyInfo, typeof(AxisElementAttribute)))
                        {
                            object val = propertyInfo.GetValue(axis, null);

                            if (val != null)
                            {
                                xmlTextWriter.WriteAttributeString(
                                    propertyInfo.Name.ToLower(), this.ParseToXmlString(val));
                            }
                        }
                    }

                    xmlTextWriter.WriteEndElement();
                }

                xmlTextWriter.WriteEndElement();
            }
        }

        /// <summary>
        /// 序列化数据
        /// </summary>
        /// <param name="chart">
        /// </param>
        /// <param name="xmlTextWriter">
        /// </param>
        private void SerializeData(SingleSeriesChart chart, XmlTextWriter xmlTextWriter)
        {
            if (chart.Set != null && chart.Set.Rows.Count > 0)
            {
                xmlTextWriter.WriteStartElement("data");
                foreach (DataRow set in chart.Set.Rows)
                {
                    xmlTextWriter.WriteStartElement("set");

                    foreach (DataColumn column in chart.Set.Columns)
                    {
                        xmlTextWriter.WriteAttributeString(
                            column.ColumnName, this.ParseToXmlString(set[column.ColumnName]));
                    }

                    xmlTextWriter.WriteEndElement();
                }

                xmlTextWriter.WriteEndElement();
            }
        }

        /// <summary>
        /// 序列化图表属性
        /// </summary>
        /// <param name="chart">
        /// </param>
        /// <param name="type">
        /// </param>
        /// <param name="xmlTextWriter">
        /// </param>
        private void SerializeProperty(SingleSeriesChart chart, Type type, XmlTextWriter xmlTextWriter)
        {
            foreach (PropertyInfo propertyInfoSup in type.GetProperties())
            {
                if (this.HasAttribute(propertyInfoSup, typeof(ChartConfigAttribute)))
                {
                    object val = propertyInfoSup.GetValue(chart, null);
                    if (val != null)
                    {
                        xmlTextWriter.WriteAttributeString(propertyInfoSup.Name.ToCamel(), this.ParseToXmlString(val));
                    }
                }
                else
                {
                    foreach (FieldInfo fieldInfo in
                        propertyInfoSup.PropertyType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
                    {
                        if (this.HasAttribute(fieldInfo, typeof(GraphElementAttribute)))
                        {
                            object val = fieldInfo.GetValue(propertyInfoSup.GetValue(chart, null));

                            if (val != null)
                            {
                                xmlTextWriter.WriteAttributeString(fieldInfo.Name, this.ParseToXmlString(val));
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 序列化图表
        /// </summary>
        /// <param name="chart">
        /// </param>
        /// <param name="xmlTextWriter">
        /// </param>
        private void SerializeSeries(SingleSeriesChart chart, XmlTextWriter xmlTextWriter)
        {
            if (chart.Series != null && chart.Series.Count > 0)
            {
                xmlTextWriter.WriteStartElement("series");
                foreach (SeriesBase column in chart.Series)
                {
                    Type typeColumn = column.GetType();

                    if (typeColumn.BaseType == typeof(StackBase))
                    {
                        object[] attributes = typeColumn.GetCustomAttributes(typeof(ColumnAttribute), true);

                        Type attrType = attributes[0].GetType();
                        var columnName = (string)attrType.GetProperty("ColumnName").GetValue(attributes[0], null);

                        xmlTextWriter.WriteStartElement(columnName);
                        foreach (PropertyInfo propertyInfo in typeColumn.GetProperties())
                        {
                            if (this.HasAttribute(propertyInfo, typeof(ColumnElementAttribute)))
                            {
                                object val = propertyInfo.GetValue(column, null);

                                if (val != null)
                                {
                                    string value = this.ParseToXmlString(val);

                                    xmlTextWriter.WriteAttributeString(propertyInfo.Name.ToCamel(), value);
                                }
                            }
                        }

                        var series = (StackBase)column;
                        if (series.Stacks != null && series.Stacks.Count > 0)
                        {
                            foreach (Stack stack in series.Stacks)
                            {
                                xmlTextWriter.WriteStartElement("stack");
                                foreach (PropertyInfo propertyInfo in stack.GetType().GetProperties())
                                {
                                    if (this.HasAttribute(propertyInfo, typeof(ColumnElementAttribute)))
                                    {
                                        object val = propertyInfo.GetValue(stack, null);

                                        if (val != null)
                                        {
                                            string value = this.ParseToXmlString(val);

                                            xmlTextWriter.WriteAttributeString(propertyInfo.Name.ToCamel(), value);
                                        }
                                    }
                                }

                                xmlTextWriter.WriteEndElement();
                            }
                        }

                        xmlTextWriter.WriteEndElement();
                    }
                    else
                    {
                        object[] attributes = typeColumn.GetCustomAttributes(typeof(ColumnAttribute), true);

                        Type attrType = attributes[0].GetType();
                        var columnName = (string)attrType.GetProperty("ColumnName").GetValue(attributes[0], null);

                        xmlTextWriter.WriteStartElement(columnName);

                        foreach (PropertyInfo propertyInfo in typeColumn.GetProperties())
                        {
                            if (this.HasAttribute(propertyInfo, typeof(ColumnElementAttribute)))
                            {
                                object val = propertyInfo.GetValue(column, null);

                                if (val != null)
                                {
                                    string value = this.ParseToXmlString(val);

                                    xmlTextWriter.WriteAttributeString(propertyInfo.Name.ToCamel(), value);
                                }
                            }
                        }

                        xmlTextWriter.WriteEndElement();
                    }
                }

                xmlTextWriter.WriteEndElement();
            }
        }

        #endregion
    }
}